import 'dart:convert';

import 'package:flutter_template/config/consts.dart';
import 'package:http/http.dart' as http;
import 'package:http_query_string/http_query_string.dart';

/// 后端响应的数据结构
class _BackendResponse<T> {
  late T data;
  late String code;
  late String message;

  _BackendResponse<T> fromMap(Map<String, Object?> map) {
    data = map["data"] as T;
    code = map["code"] as String;
    message = map["message"] as String;

    return this;
  }
}

// 用于发送各类 http 请求，并处理响应结果
class Request {
  Request._privateConstructor();

  static final Request _instance = Request._privateConstructor();

  static Request get instance {
    return _instance;
  }

  String _getFullPath(String path) {
    return ("${Consts.httpServer}/$path").replaceAll("//", "/");
  }

  String _pathWithQuery(String path, Map<String, dynamic>? params) {
    if (params == null) {
      return path;
    }

    return "$path?${Encoder().convert(params)}";
  }

  Uri _getRequestUrl(String path, Map<String, dynamic>? params) {
    String fullPath = _getFullPath(_pathWithQuery(path, params));
    Uri uri = Uri.parse(fullPath);
    return uri;
  }

  T? _unwrapResponse<T>(http.Response response) {
    if (response.body.isEmpty) {
      return null;
    }

    var bodyMap = json.decode(response.body);
    _BackendResponse<T?> respBody = _BackendResponse<T>().fromMap(bodyMap);

    return respBody.data;
  }

  /// 发送 http HEAD 请求
  ///
  /// [path] 请求地址的路径
  ///
  /// [params] http HEAD 请求的查询参数
  Future<T> head<T>(
    String path, {
    Map<String, dynamic>? params,
    Map<String, String>? headers,
  }) async {
    Uri uri = _getRequestUrl(path, params);
    var response = await http.head(uri, headers: headers);
    return _unwrapResponse(response);
  }

  /// 发送 http GET 请求
  ///
  /// [path] 请求地址的路径
  ///
  /// [params] http GET 请求的查询参数
  Future<T> get<T>(
    String path, {
    Map<String, dynamic>? params,
    Map<String, String>? headers,
  }) async {
    Uri uri = _getRequestUrl(path, params);
    var response = await http.get(uri, headers: headers);
    return _unwrapResponse(response);
  }

  /// 发送 http POST 请求
  ///
  /// [path] 请求地址的路径
  ///
  /// [params] http POST 请求的查询参数
  ///
  /// [data] http POST 请求发送的数据
  Future<T> post<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    Encoding? encoding,
  }) async {
    Uri uri = _getRequestUrl(path, params);

    var response = await http.post(
      uri,
      body: data,
      headers: headers,
      encoding: encoding,
    );

    return _unwrapResponse(response);
  }

  /// 发送 http PUT 请求
  ///
  /// [path] 请求地址的路径
  ///
  /// [params] http PUT 请求的查询参数
  ///
  /// [data] http PUT 请求发送的数据
  Future<T> put<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    Encoding? encoding,
  }) async {
    Uri uri = _getRequestUrl(path, params);

    var response = await http.put(
      uri,
      body: data,
      headers: headers,
      encoding: encoding,
    );

    return _unwrapResponse(response);
  }

  /// 发送 http PATCH 请求
  ///
  /// [path] 请求地址的路径
  ///
  /// [params] http PATCH 请求的查询参数
  ///
  /// [data] http PATCH 请求发送的数据
  Future<T> patch<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    Encoding? encoding,
  }) async {
    Uri uri = _getRequestUrl(path, params);

    var response = await http.patch(
      uri,
      body: data,
      headers: headers,
      encoding: encoding,
    );

    return _unwrapResponse(response);
  }

  /// 发送 http DELETE 请求
  ///
  /// [path] 请求地址的路径
  ///
  /// [params] http DELETE 请求的查询参数
  ///
  /// [data] http DELETE 请求发送的数据
  Future<T> delete<T>(
    String path, {
    Object? data,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    Encoding? encoding,
  }) async {
    Uri uri = _getRequestUrl(path, params);

    var response = await http.delete(
      uri,
      body: data,
      headers: headers,
      encoding: encoding,
    );

    return _unwrapResponse(response);
  }
}
